﻿using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Input;
using VisualWip.Core;
using VisualWip.Core.Columns;
using VisualWip.Core.Utils;
using Application = VisualWip.Core.Application;

namespace VisualWip.PresentationModel
{
    public abstract class ColumnViewModelBase : INotifyPropertyChanged
    {
        protected readonly Application Application;
        private readonly Column _column;
        private readonly IColumnBuilder _columnBuilder;
        private ICommand _backwardCommand;
        private ICommand _closeCommand;
        private ICommand _forwardCommand;
        private bool _isRefreshing;
        private ICommand _settingsCommand;
        private bool _isPanningOrZooming;

        protected ColumnViewModelBase(Application application, Column column, IColumnBuilder columnBuilder)
        {
            Application = application;
            _column = column;
            _columnBuilder = columnBuilder;
            Application.PropertyChanged += ApplicationPropertyChanged;
            _column.PreRefresh += ColumnPreRefresh;
            _column.Refreshing += ColumnRefreshing;
            _column.RefreshDone += ColumnRefreshDone;
            _column.PropertyChanged += ColumnPropertyChanged;
        }

        public virtual string ColumnName
        {
            get { return _column.ColumnName; }
            set
            {
                if (value != _column.ColumnName)
                {
                    _column.ColumnName = value;
                    OnPropertyChanged(this, () => ColumnName);
                }
            }
        }

        public virtual ColumnType ColumnType
        {
            get { return _column.ColumnType; }
            set
            {
                if (value != _column.ColumnType)
                {
                    _column.ColumnType = value;
                    OnPropertyChanged(this, () => ColumnType);
                }
            }
        }

        public virtual int PositionX
        {
            get { return _column.PositionX; }
            set
            {
                if (value != _column.PositionX)
                {
                    _column.PositionX = value;
                    OnPropertyChanged(this, () => PositionX);
                }
            }
        }

        public virtual int PositionY
        {
            get { return _column.PositionY; }
            set
            {
                if (value != _column.PositionY)
                {
                    _column.PositionY = value;
                    OnPropertyChanged(this, () => PositionY);
                }
            }
        }

        public virtual int Width
        {
            get { return _column.Width; }
            set
            {
                if (value != _column.Width)
                {
                    _column.Width = value;
                    OnPropertyChanged(this, () => Width);
                    OnPropertyChanged(this, () => ActualWidth);
                }
            }
        }

        public virtual int ActualWidth
        {
            get { return _column.Width; }
        }

        public virtual int Height
        {
            get { return _column.Height; }
            set
            {
                if (value != _column.Height)
                {
                    _column.Height = value;
                    OnPropertyChanged(this, () => Height);
                    OnPropertyChanged(this, () => ActualHeight);
                }
            }
        }

        public virtual int ActualHeight
        {
            get { return _column.Height; }
        }

        public virtual int ZIndex
        {
            get { return _column.ZIndex; }
            set
            {
                if (value != _column.ZIndex)
                {
                    _column.ZIndex = value;
                    OnPropertyChanged(this, () => ZIndex);
                }
            }
        }

        public virtual ICommand ForwardCommand
        {
            get
            {
                if (_forwardCommand == null)
                {
                    _forwardCommand = new RelayCommand(param => Forward());
                }
                return _forwardCommand;
            }
        }

        public virtual ICommand BackwardCommand
        {
            get
            {
                if (_backwardCommand == null)
                {
                    _backwardCommand = new RelayCommand(param => Backward(), param => CanBackward);
                }
                return _backwardCommand;
            }
        }

        private bool CanBackward
        {
            get { return ZIndex > 0; }
        }


        public virtual ICommand CloseCommand
        {
            get
            {
                if (_closeCommand == null)
                {
                    _closeCommand = new RelayCommand(param => OnClose());
                }
                return _closeCommand;
            }
        }

        public virtual ICommand SettingsCommand
        {
            get
            {
                if (_settingsCommand == null)
                {
                    _settingsCommand = new RelayCommand(param => Settings(), param => CanSettings);
                }
                return _settingsCommand;
            }
        }

        protected bool CanSettings
        {
            get { return true; }
        }

        public virtual bool IsRefreshing
        {
            get { return _isRefreshing; }
            private set
            {
                if (value != _isRefreshing)
                {
                    _isRefreshing = value;
                    OnPropertyChanged(this, () => IsRefreshing);
                }
            }
        }

        public virtual Visibility SizeAndPositionAdornerVisibility
        {
            get { return Application.IsDesignMode && !IsPanningOrZooming ? Visibility.Visible : Visibility.Collapsed; }
        }

        public bool IsPanningOrZooming
        {
            get {
                return _isPanningOrZooming;
            }
            set {
                _isPanningOrZooming = value;
                OnPropertyChanged(this,()=> IsPanningOrZooming);
                OnPropertyChanged(this, ()=> SizeAndPositionAdornerVisibility);
            }
        }

        public bool SnapToGrid
        {
            get {
                return Application.SnapToGrid;
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        protected virtual void ColumnPreRefresh(object sender, EventArgs e)
        {
            OnPreRefresh();
        }

        protected virtual void ColumnRefreshing(object sender, EventArgs e)
        {
            IsRefreshing = true;
            OnRefreshing();
        }

        protected virtual void ColumnRefreshDone(object sender, EventArgs e)
        {
            IsRefreshing = false;
            OnRefreshDone();
        }

        public event EventHandler PreRefresh;
        public event EventHandler Refreshing;
        public event EventHandler RefreshDone;

        protected virtual void OnPreRefresh()
        {
            if (PreRefresh != null)
                PreRefresh(this, EventArgs.Empty);
        }

        protected virtual void OnRefreshing()
        {
            if (Refreshing != null)
                Refreshing(this, EventArgs.Empty);
        }

        protected virtual void OnRefreshDone()
        {
            if (RefreshDone != null)
                RefreshDone(this, EventArgs.Empty);
        }

        protected virtual void ColumnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
        }


        private void Backward()
        {
            ZIndex -= 1;
        }

        private void Forward()
        {
            ZIndex += 1;
        }

        private void Settings()
        {
            _columnBuilder.OpenSettings(_column);
        }

        private void ApplicationPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == PropertyHelper.Name(() => Application.IsDesignMode))
                OnPropertyChanged(this, () => SizeAndPositionAdornerVisibility);
            if (e.PropertyName == PropertyHelper.Name(() => Application.SnapToGrid))
                OnPropertyChanged(this, () => SnapToGrid);
        }

        private void OnClose()
        {
            Application.RemoveColumn(_column);
        }


        protected void BubbleOnPropertyChanged<T>(PropertyChangedEventArgs propertyChangedEventArgs, object sender,
                                                  Expression<Func<T>> property)
        {
            if (propertyChangedEventArgs.PropertyName == PropertyHelper.Name(property))
            {
                OnPropertyChanged(sender, property);
            }
        }

        protected void OnPropertyChanged<T>(object sender, Expression<Func<T>> property)
        {
            PropertyHelper.OnPropertyChanged(sender, PropertyChanged, property);
        }
    }
}